課程:RTK Query 資料管理 第 1 堂:RTK Query 概念建立
31架構與定位
既然我們已經知道傳統的 useEffect + fetch 模式在處理非同步資料時會遇到重複請求、快取缺失等種種痛點,你可能會問:「我已經學過 Redux Toolkit(RTK)了,為什麼不能直接用 createSlice 搭配 createAsyncThunk 來解決這些問題?為什麼 Redux 官方要特別推出 RTK Query 這個工具?」
答案在於:並不是所有的「狀態」都是平等的。
在進入程式碼實作之前,我們必須先釐清 RTK Query 在整個應用程式中的定位,以及它與你已經熟悉的 createSlice 究竟有什麼不同。這將幫助你建立正確的心智模型,避免在未來的專案中過度設計或放錯了資料的位置。
本地客戶端狀態 vs. 遠端伺服器狀態
在現代前端開發中,我們管理的資料可以被分為兩大陣營。理解這兩者的差異,是學好 RTK Query 的第一步。
1. 本地客戶端狀態 (Local Client State)
這是由你的應用程式 完全擁有 並控制的狀態。
- 來源:使用者操作或 UI 邏輯。
- 特性:它是同步的,且你是唯一的「事實來源(Source of Truth)」。
- 範例:深色模式的切換開關、側邊欄是否開啟、目前正在填寫但尚未送出的表單內容、分頁組件的當前頁碼。
- 管理工具:這正是
createSlice(RTK) 或useState(React) 大顯身手的地方。
2. 遠端伺服器狀態 (Remote Server State)
這是儲存在伺服器端,你的應用程式 只是暫時借用 並顯示的狀態。
- 來源:外部 API、資料庫。
- 特性:它是非同步的。當你顯示這份資料時,伺服器上的原始資料可能已經被其他使用者修改了(這就是為什麼需要快取與失效機制)。
- 範例:使用者的個人資料、部落格文章列表、電商網站的庫存數量。
- 管理工具:這就是 RTK Query 專門解決的領域。
對比表格:為什麼我們需要兩套工具?
| 特性 | 本地狀態 (createSlice) | 遠端狀態 (RTK Query) |
|---|---|---|
| 主導權 | 前端應用程式完全控制 | 伺服器控制,前端僅是快取複本 |
| 生命週期 | 隨應用程式或組件啟動而產生 | 需要透過網路請求獲取,有載入中/錯誤狀態 |
| 資料同步 | 始終是最新(因為是你改的) | 可能會過時 (Stale),需要定期刷新 |
| 主要挑戰 | 狀態邏輯的複雜度、效能優化 | 網路延遲、快取管理、請求去重複 |
兩者並存的場景:以「商品搜尋頁面」為例
想像你在開發一個電商平台。使用者輸入關鍵字,點擊搜尋,然後看到一列商品。
- 搜尋關鍵字 (
**searchTerm**):這應該存在createSlice管理的本地狀態中。因為這是使用者當下的輸入行為,不需要等 API 回傳。 - 搜尋結果列表 (
**products**):這應該交給 RTK Query。當searchTerm改變時,RTK Query 會去抓取資料,並幫你處理「載入中」的轉圈圈、快取之前的搜尋結果、以及處理如果 API 壞掉時的錯誤訊息。
思考一下:如果把「搜尋結果」也塞進 createSlice,你得手寫 isLoading、error 變數,還得處理「如果使用者短時間內點了兩次搜尋,要怎麼取消前一次請求」的麻煩事。而這些,RTK Query 通通內建了。
RTK Query 的四大核心組成 (createApi)
當我們使用 RTK Query 時,所有的核心設定都會集中在一個叫做 createApi 的函式中。你可以把它想像成是在定義一張「API 地圖」。
這張地圖主要由四個部分組成:
1. reducerPath:命名空間
這是一個字串,用來定義這組 API 狀態在 Redux Store 中的「位置」。
就像你在 configureStore 裡定義不同的 slice 一樣,reducerPath 確保了 RTK Query 的快取資料不會跟你的本地狀態混在一起。
- 語意:它是這份 API 快取在 Store 樹狀結構中的根節點名稱。
2. baseQuery:請求基底
這是所有請求的基礎設定。絕大多數情況下,我們會使用 RTK Query 內建的 fetchBaseQuery。
- 職責:設定 API 的根路徑 (baseUrl)、統一處理 Header(例如注入 Authorization Token)、以及處理基本的請求與回應攔截。
- 比喻:它就像是公司的「收發室」,所有出去的信件都要先經過這裡蓋章。
3. endpoints:端點定義
這是你定義具體 API 行為的地方。每一個 endpoint 就像是地圖上的一個目的地。
- 兩種類型:
query:用於獲取資料 (GET)。
mutation:用於修改資料 (POST, PUT, DELETE)。- 語意:在這裡你定義了「請求的 URL 是什麼」、「需要帶什麼參數」、「回傳的資料型別是什麼」。
4. 自動生成的 Hooks
這是 RTK Query 最讓開發者驚艷的地方。
只要你在 endpoints 裡定義了一個叫 getPosts 的 query,RTK Query 就會自動生成一個對應的 React Hook,叫做 useGetPostsQuery。
- 價值:你不需要手動 dispatch action,不需要寫
useEffect。直接在組件裡呼叫這個 Hook,它就會自動幫你發請求、回傳資料、並提供isLoading等狀態。
理解「RTK Query 實質上是一個特別的 Slice」
對於已經熟悉 createSlice 的你來說,切換到 RTK Query 可能會覺得這是一個完全不同的外星科技。但其實,RTK Query 的底層依然是 Redux。
當你呼叫 createApi 時,它在背後幫你做了以下幾件事:
- 自動生成一個 Slice:它幫你定義好了存放 API 回應、載入狀態、錯誤訊息的 state 結構。
- 自動生成 Reducers:當網路請求開始、成功或失敗時,它有一套內建的邏輯來更新 state。
- 自動生成 Actions:每一種請求狀態都有對應的 Action 被發出。
- 快取管理邏輯:它幫你寫好了「如果 Store 裡已經有這份資料,且還沒過期,就不要發請求」的複雜邏輯。
這就是為什麼我們說 RTK Query 是「宣告式(Declarative)」的。
以前用 createAsyncThunk,你得像寫食譜一樣,一步步教電腦怎麼抓資料、怎麼處理錯誤;現在用 RTK Query,你只需要告訴電腦「我的 API 在哪裡,長什麼樣子」,剩下的繁瑣步驟它都幫你封裝好了。
這種「平滑過渡」的意義
這意味著你現有的 Redux 知識完全沒有白費。
- 你依然可以用 Redux DevTools 監控所有的 API 請求(你會看到 RTK Query 發出的專屬 Actions)。
- 你的 API 狀態依然存在於那個單一的 Store 中,保持了「單一事實來源」的優良傳統。
- 你可以同時在一個專案中使用
createSlice處理 UI 狀態,並用createApi處理資料請求,兩者分工明確,互不干擾。
總結與核心心智模型
讓我們用一個簡單的邏輯來總結這一部分:
- 分工明確:
createSlice管 UI 狀態(你是主人);createApi管伺服器資料(你是租客)。 - 單一事實來源:即使 RTK Query 看起來像是一個獨立的模組,它的資料最終還是會匯流到 Redux Store 中,讓你能統一管理。
- 自動化生產線:透過
createApi定義好規則,它就為你產出 Hooks,消除掉所有重複的非同步樣板程式碼。
建立你的心智模型
在進入下一章實作之前,請記住這個畫面:
你的應用程式是一個大的辦公室。createSlice 是你的辦公桌抽屜,放著你隨手要用的文具(UI 狀態);而 createApi 是你連往外部圖書館的終端機,它幫你檢索資訊、快取書籍,並在書本內容更新時通知你,而你只需要在終端機前下指令(Hooks)即可。
承先啟後
我們已經建立了對 RTK Query 架構的深層認識,知道它與傳統 Slice 的分工。但這台強大的「資料終端機」要如何正式連接到我們的 Redux 體系中呢?
在下一個章節中,我們將深入探討 「整合至 Redux Store」 的細節。我們會看到 api.reducer 該如何掛載,以及最關鍵的 api.middleware 究竟在後台偷偷做了哪些強大的工作,例如快取過期管理與訂閱計數。這將為我們動手寫下第一行 createApi 程式碼打下最後一塊基石。